package cn.edu.cug.cs.gtl.docsrv.shp.manager;

import cn.edu.cug.cs.gtl.common.Pair;
import cn.edu.cug.cs.gtl.docsrv.shp.querier.BlockQuerier;
import cn.edu.cug.cs.gtl.docsrv.shp.querier.DefaultBlockQuerier;
import cn.edu.cug.cs.gtl.docsrv.shp.updater.BlockUpdater;
import cn.edu.cug.cs.gtl.docsrv.shp.updater.DefaultBlockUpdater;
import cn.edu.cug.cs.gtl.extractor.ShapeExtractor;
import cn.edu.cug.cs.gtl.io.FileDataSplitter;
import cn.edu.cug.cs.gtl.mybatis.Session;
import cn.edu.cug.cs.gtl.mybatis.mapper.shp.*;
import cn.edu.cug.cs.gtl.mybatis.metadata.ShpUtils;
import cn.edu.cug.cs.gtl.protos.TimeValue;
import cn.edu.cug.cs.gtl.protoswrapper.TimeValueWrapper;
import cn.edu.cug.cs.gtl.util.ZipUtils;
import org.geotools.data.shapefile.files.ShpFileType;
import org.geotools.data.shapefile.files.ShpFiles;
import org.locationtech.jts.geom.Envelope;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.math.BigDecimal;
import java.util.*;

/**
 * 将图形的元数据与图形文件的ZIP压缩存放在一个数据表SHP_ALL中，
 * 这种方式对于小数据量比较有效
 */
public class RDBShpManagerForSingleTable extends ShpManager{
    /**
     * 构造函数
     *
     * @param session
     */
    public RDBShpManagerForSingleTable(Session session) {
        super(session);
    }

    /**
     * 将一个由shpItem+shpFile组成的图形文档插入数据库
     *
     * @param shpItem 图形元数据信息
     * @param shpFile 图形文件内容
     * @return
     */
    @Override
    public boolean insert(ShpItem shpItem, ShpFile shpFile) {
        return BlockUpdater.insert(session,ShpUtils.toShpAll(shpItem,shpFile));
    }

    /**
     * 将ShpAll插入数据库
     * @param shpAll
     * @return
     */
    @Override
    public boolean insert(ShpAll shpAll) {
        return BlockUpdater.insert(session,shpAll);
    }

    /**
     * 读取本地目录中的所有图形文档，将其插入图形库中
     *
     * @param localDir 本地目录
     * @return 成功返回true, 否则返回false
     */
    @Override
    public boolean insert(String localDir) {
        try{
            File file = new File(localDir);
            if (!file.isDirectory())
                return false;
            File[] files = file.listFiles();
            for (File f : files) {
                if(f.isDirectory()){
                    return insert(f.getCanonicalPath());
                }

                if (!f.getName().endsWith("shp"))
                    continue;

                ShpFiles shpFiles = new ShpFiles(f.toURI().toURL());
                //检查是否存DBF、SHX和SHP
                if(!shpFiles.exists(ShpFileType.SHX) || !shpFiles.exists(ShpFileType.DBF))
                    continue;
                //获取该SHP文件的边界矩形
                Envelope envelope = ShapeExtractor.parseToEnvelope(f.getCanonicalPath());
                //将DBF、SHX和SHP压缩成ZIP文件，并读取文件内容为byte[]
                List<File> fileToArchive = new ArrayList<>();
                fileToArchive.add(new File(shpFiles.get(ShpFileType.SHP)));
                fileToArchive.add(new File(shpFiles.get(ShpFileType.DBF)));
                fileToArchive.add(new File(shpFiles.get(ShpFileType.SHX)));
                ByteArrayOutputStream zipBytes = new ByteArrayOutputStream();
                ZipUtils.zip(fileToArchive,zipBytes);

                //设置ShpAll
                ShpAllMapper shpAllMapper = session.getMapper(ShpAllMapper.class);
                ShpAll shpAll = new ShpAll();
                shpAll.setShpInternalId(shpAllMapper.nextInternalId());
                shpAll.setShpEnTitle(f.getName());
                shpAll.setShpExtent(
                        new StringBuffer()
                                .append(String.valueOf(envelope.getMinX()))
                                .append(FileDataSplitter.COMMA.getDelimiter())
                                .append(String.valueOf(envelope.getMaxX()))
                                .append(FileDataSplitter.COMMA.getDelimiter())
                                .append(String.valueOf(envelope.getMinY()))
                                .append(FileDataSplitter.COMMA.getDelimiter())
                                .append(String.valueOf(envelope.getMaxY()))
                                .toString()
                );
                shpAll.setShpCreateDate(new Date());
                shpAll.setShpAppendDate(new Date());
                shpAll.setShpType("SHP");
                shpAll.setShpRawBytes(zipBytes.toByteArray());
                //插入数据库
                BlockUpdater.insert(session,shpAll);
                zipBytes.close();
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
        return true;
    }

    /**
     * 根据shpInternalId删除数据库中对应的图形文档记录
     *
     * @param shpItem 必须填充shpInternalId
     * @return
     */
    @Override
    public boolean delete(ShpItem shpItem) {
        ShpAll shpAll = new ShpAll();
        shpAll.setShpInternalId(shpItem.getShpInternalId());
        return BlockUpdater.delete(session,shpAll);
    }

    /**
     *
     * @param shpAll 必须填充shpInternalId
     * @return
     */
    @Override
    public boolean delete(ShpAll shpAll) {
        return BlockUpdater.delete(session,shpAll);
    }

    /**
     * 根据shpItem+shpFile更新数据库中的图形文档记录
     *
     * @param shpItem 该对象的shpInternalId必须填充
     * @param shpFile 更新文档文件的内容
     * @return 更新成功返回true, 否则返回false
     */
    @Override
    public boolean update(ShpItem shpItem, ShpFile shpFile) {
        ShpAll shpAll = ShpUtils.toShpAll(shpItem,shpFile);
        return BlockUpdater.update(session,shpAll);
    }

    /**
     *
     * @param shpAll 该对象的shpInternalId必须填充
     * @return
     */
    @Override
    public boolean update(ShpAll shpAll) {
        return BlockUpdater.update(session,shpAll);
    }

    /**
     *
     * @param shpInternalId
     * @return
     */
    @Override
    public ShpAll queryByInternalId(BigDecimal shpInternalId) {
        ShpAllMapper shpAllMapper = session.getMapper(ShpAllMapper.class);
        ShpAll shpAll = shpAllMapper.selectByPrimaryKey(shpInternalId);
        session.commit();
        return shpAll;
    }

    /**
     *
     * @param shpEnTitle
     * @return
     */
    @Override
    public List<ShpAll> queryByEnTitle(String shpEnTitle){
        ShpAllMapper shpAllMapper = session.getMapper(ShpAllMapper.class);
        List<ShpAll> shpAlls =  shpAllMapper.selectByEnTitle(shpEnTitle);
        session.commit();
        return shpAlls;
    }

    /**
     *
     * @param shpCnTitle  文档注释
     * @return
     */
    @Override
    public List<ShpAll> queryByComment(String shpCnTitle) {
        ShpAllMapper shpAllMapper = session.getMapper(ShpAllMapper.class);
        List<ShpAll> shpAlls =  shpAllMapper.selectByCnTitle(shpCnTitle);
        session.commit();
        return shpAlls;
    }

    /**
     *
     * @param shpType
     * @return
     */
    @Override
    public List<ShpAll> queryByType(String shpType) {
        ShpAllMapper shpAllMapper = session.getMapper(ShpAllMapper.class);
        List<ShpAll> shpAlls =  shpAllMapper.selectByType(shpType);
        session.commit();
        return shpAlls;
    }

    /**
     *
     * @param begin
     * @param end
     * @return
     */
    @Override
    public List<ShpAll> queryByAppendDate(TimeValue begin, TimeValue end) {
        ShpAllMapper shpAllMapper = session.getMapper(ShpAllMapper.class);
        List<ShpAll> shpAlls =  shpAllMapper.selectByAppendDate(TimeValueWrapper.toUtilDate(begin),TimeValueWrapper.toUtilDate(end));
        session.commit();
        return shpAlls;
    }

    /**
     * 根据传入参数在图形库中进行查询
     *       try {
     *             Map<String,Object> p = new LinkedHashMap<String,Object>();
     *             p.put("shpInternalId",BigDecimal.valueOf(1));
     *             List<ShpItem> r1 = this.query(p);
     *             p.clear();
     *             p.put("shpEnTile","aaa");
     *             p.put("shpCnTile","zhangsan");
     *             List<ShpItem> r2 = this.query(p);
     *             boolean b = r1.size()==r2.size();
     *         }
     *         catch (Exception e){
     *             e.printStackTrace();
     *         }
     * @param parameters
     *     BigDecimal shpInternalId;
     *
     *     String shpEnTitle;
     *
     *     String shpCnTitle;
     *
     *     String shpUuid;
     *
     *     String shpVersion;
     *
     *     String shpKeywords;
     *
     *     String shpAbstract;
     *
     *     String shpContent;
     *
     *     String shpType;
     *
     *     String shpAuthors;
     *
     *     String shpAffiliation;
     *
     *     String shpTags;
     *
     *     String shpUrl;
     *
     *     Date shpAppendDate;
     *
     *     Date shpUpdateDate;
     *
     *     Date shpCreateDate;
     *
     *     String shpMemo;
     *
     *     BigDecimal shpCatalog;
     *
     *     String shpExtent;
     *
     *     byte[] shpRawBytes;
     * @return 返回查询到的文档。
     */
    @Override
    public List<ShpAll> query(Map<String, Object> parameters) {
        ShpAllMapper shpAllMapper = session.getMapper(ShpAllMapper.class);
        return shpAllMapper.query(parameters);
    }

    /**
     * 获取图形块ShapeBlock级别的查询器
     *
     * @return
     */
    @Override
    public BlockQuerier getBlockQuerier() {
        return new DefaultBlockQuerier(this);
    }

    /**
     * 获取图形块ShapeBlock级别的更新器
     * @return
     */
    @Override
    public BlockUpdater getBlockUpdater() {
        return new DefaultBlockUpdater(this);
    }

    /**
     * 查询得到所有ShapeBlock的边界矩形
     * @return
     */
    @Override
    public  List<Pair<BigDecimal, Envelope>> queryAllEnvelopes(){
        try {
            ShpAllMapper shpAllMapper = session.getMapper(ShpAllMapper.class);
            List<LinkedHashMap<String, Object>> ls = shpAllMapper.selectAllInternalIdAndExtents();
            System.out.println(ls.size());
            List<Pair<BigDecimal, Envelope>> r = new ArrayList<>();
            for (LinkedHashMap<String, Object> l: ls){
                BigDecimal id = (BigDecimal)l.get("SHP_INTERNAL_ID");
                String coords = (String)l.get("SHP_EXTENT");
                String [] cs = coords.split(FileDataSplitter.COMMA.getDelimiter());
                double minX = Double.valueOf(cs[0]).doubleValue();
                double maxX = Double.valueOf(cs[1]).doubleValue();
                double minY = Double.valueOf(cs[2]).doubleValue();
                double maxY = Double.valueOf(cs[3]).doubleValue();
                Envelope envelope = new Envelope(minX,maxX,minY,maxY);
                r.add(new Pair<>(id,envelope));
            }
            return r;
        }
        catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}
